package ms.jacrim.pddl.evaluator;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import ms.jacrim.pddl.PddlSolution;
import ms.jacrim.tropos.Composition;
import ms.jacrim.tropos.Diagram;
import ms.jacrim.tropos.Service;
import ms.jacrim.tropos.TroposObject;
import ms.utils.Shell;

import org.w3c.dom.Element;

public class RiskAnalysis extends Evaluator {

	private String tempFolder;
	private String cmd;
	private float totalSat;
	private float totalDen;
	
	public static class InputGenerator {

		private PddlSolution solution;
		private List<TroposObject> nodes = new ArrayList<TroposObject>();
		private List<String> nodeMap = new ArrayList<String>();
		
		public InputGenerator(PddlSolution solution) {
			this.solution = solution;
		}
		
		public int getNodeIndex(TroposObject node) {
			return nodes.indexOf(node);
		}
		
		public String generate2() throws Exception {
			String result = "";
			
			StringBuilder relations = new StringBuilder();
			
			Diagram d = solution.SolutionDiagram();
			
			for (Service s : d.getServices()) {
				nodes.add(s);
			}
			
			List<TroposObject> processedNode = new ArrayList<TroposObject>();
			
			for (Composition rel : d.getObjects(Composition.class)) {
				if (processedNode.indexOf(rel.getSource()) < 0) {
					processedNode.add(rel.getSource());
					List<TroposObject> childrent = findChildObject(rel.getSource());
					relations.append(String.format("R %s %d ", rel.getCompositionType().toString(), getNodeIndex(rel.getSource())));
					
					for(TroposObject child: childrent) {
						relations.append(getNodeIndex(child));
						relations.append(' ');
					}
					
					relations.append("\r\n");
				}
					
			}
			return result;
		}
		
		public String generateInput() {
			StringBuilder result = new StringBuilder();
			StringBuilder relation = new StringBuilder();
			
			int relationCount = 0;
			
			for (int i = 0; i < solution.ActionCount(); i++) {
				PddlSolution.Action a = solution.getAction(i);
				if ("SATISFIES".equalsIgnoreCase(a.Name()))
					resolveNodeIndex(a.Argument(0) + "." + a.Argument(1));
				else if ("PASSES".equalsIgnoreCase(a.Name())) {
					resolveNodeIndex(a.Argument(0) + "." + a.Argument(2));
					resolveNodeIndex(a.Argument(1) + "." + a.Argument(2));
				}
				else if (a.Name().startsWith("AND_") || a.Name().startsWith("OR_")) {
					int parent = resolveNodeIndex(a.Argument(0) + "." + a.Argument(1));
					relation.append(String.format("R %s %d", a.Name().substring(0, 3), parent));
					
					for (int j = 2; j < a.ArgumentCount(); j++)
						relation.append(" " + resolveNodeIndex(a.Argument(0) + "." + a.Argument(j)));
					
					relation.append("\r\n");
					relationCount++;
				}
			} 
			
			result.append(String.format("C %d %d\r\n", nodeMap.size(), relationCount));
			for (int i = 0; i < nodeMap.size(); i++)
				result.append(String.format("N %.1f %.1f\r\n", 0.5f, 0.5f));
			
			result.append(relation.toString());
			return result.toString();
		}
		
		public String generateMap() {
			StringBuilder result = new StringBuilder();
			for (int i = 0; i < nodeMap.size(); i++)
				result.append(String.format("%d -> %s\r\n", i, nodeMap.get(i)));
			return result.toString();
		}
		
		private int resolveNodeIndex(String name) {
			int result = nodeMap.indexOf(name);
			if (result < 0)
				nodeMap.add(name);
			return result >= 0 ? result : nodeMap.size() - 1;
		}
		
		private List<TroposObject> findChildObject(TroposObject parent) {
			List<TroposObject> result = new ArrayList<TroposObject>();
			for (Composition rel : parent.getDiagram().getObjects(Composition.class)) {
				if (rel.getSource() == parent)
					result.add(rel.getTarget());
			}
			return result;
		}
	}
	
	public RiskAnalysis(float coeffience) {
		super(coeffience);
	}

	@Override
	public String Name() {
		return "QuatitativeGoalRisk";
	}

	@Override
	public void loadConfig(Element node) {
		super.loadConfig(node);
		tempFolder = node.getAttribute("temp_folder");
		cmd = node.getAttribute("solver");
		if (tempFolder.length() == 0)
			tempFolder = ".";
		if (!tempFolder.endsWith("\\"))
			tempFolder += "\\";
	}

	@Override
	public float evaluate(PddlSolution solution) throws Exception {
		generateGRinput(solution);
		
		totalSat = totalDen = 0;
		
		if (new File(tempFolder + "stdout.txt").exists()) {
			Scanner s = new Scanner(new File(tempFolder + "stdout.txt"));
			// int lineIdx = 0;
			
			while (s.hasNextLine()) {
				String line = s.nextLine();
				if (line.startsWith("N")) {
					String[] arg = line.split(" ");
					totalSat += Float.parseFloat(arg[1]);
					totalDen += Float.parseFloat(arg[2]);
				}
			}
		}
		return Score();
	}

	private void generateGRinput(PddlSolution solution) throws Exception {
		InputGenerator gen = new InputGenerator(solution);
		String filename = tempFolder + "solverInput.txt";
		FileWriter fw = new FileWriter(filename);
		fw.write(gen.generateInput());
		fw.close();
		
		fw = new FileWriter(tempFolder + "solverInput.map.txt");
		fw.write(gen.generateMap());
		fw.close();
		
		// execute GoalSolver
		Shell.execute(cmd + " " + tempFolder + " solverInput.txt");
		
	}

	@Override
	public float Score() {
		return totalSat / (totalDen != 0 ? totalDen : 1);
	}

}
